package phantm.util

import phantm.symbols._
import phantm.cfg.ControlFlowGraph
import phantm.phases.PhasesContext
import phantm.ast.{ Trees => AST }
import phantm.types._
import phantm.cfg.Trees._

class PhasesUtils(ctx : PhasesContext) {
  
  def getCFG(sym: Option[FunctionSymbol]): Option[ControlFlowGraph] = ctx.cfgs.get(sym)

  def lookupConstructor(classSymbol: ClassSymbol): Option[MethodSymbol] = {
    classSymbol.methods.get("__construct") orElse
      classSymbol.methods.get(classSymbol.name.toLowerCase) orElse
      (classSymbol.parent match {
        case Some(cs) => lookupConstructor(cs)
        case None => None
      })
  }

  def lookupMethod(optionClassSymbol: Option[ClassSymbol], methodId: AST.Identifier): Option[MethodSymbol] =
    optionClassSymbol match {
      case Some(classSymbol) =>
        classSymbol.lookupMethod(methodId.value, Some(classSymbol)) match {
          case LookupResult(Some(method: MethodSymbol), _, _) => Some(method)
          case _ => None
        }
      case None => None
    }

  def getClassFromType(typ: Option[Type], env: TypeEnvironment): Option[ClassSymbol] = typ match {
    case Some(TClass(classSymbol)) => Some(classSymbol)
    case Some(objRef @ TObjectRef(id)) => getClassFromType(Some(objRef.realObject(env).ct), env)
    case Some(union: TUnion) =>
      val filteredTypes = union.types.filter(aType => aType.isInstanceOf[TClass])
      if (filteredTypes.isEmpty) None
      else getClassFromType(Some(filteredTypes.reduceLeft(_ union _)), env)
    case _ => None
  }

  def classFromSimpleValue(sv: SimpleValue, env: TypeEnvironment): Option[ClassSymbol] = sv match {
    case Constant(cs) => getClassFromType(Some(cs.typ), env)
    case id: SimpleVariable => getClassFromType(Some(env.lookup(id).getOrElse(TTop)), env)
    case op @ ObjectProperty(obj, prop) =>
      val typ = classFromSimpleValue(obj, env)
      typ match {
        case cs @ Some(classSymbol) => prop match {
          case PHPString(value) => classSymbol.properties get value match {
            case Some(propertySymbol) => getClassFromType(Some(propertySymbol.typ), env)
            case None => None
          }
          case _ => None
        }
        case None => None
      }
    case _ => None
  }

}